Orika 使用不当导致的内存溢出问题

前言

最近突然收到了系统推送消息告警。打开一看,好家伙!直接 kafka 堆积超过了 100w 条数据,就差基础服务的人找上门来了。

kafka告警

经过简单的一系列排查,终于找到是代码中 Orika 工具使用不当导致内存增加、cpu 过高导致消费变慢。

下面记录下排查的思路


分析

出现消息堆积告警的时候,最开始的分析:

  1. 分片不均匀导致的消息堆积。因为出现告警的 topic 分片数是 8 个,服务实例只有 6 个。可能 某 个服务实例收到很多的消息,消费变慢,导致 kafka 消息堆积
  2. 流量突然增加
  3. 业务本身逻辑处理速度慢
  4. 内存增加导致 cpu 过高 消费变慢

解决过程

  1. 首先我们进行了服务实例的调整以及服务重启,但是这样并没有真正的解决问题。在服务重新运行的期间可以看见堆积量的减少,但是运行时间长后,堆积量又开始增加了。

  2. 其次通过观察业务的流量监控,也并没有发现流量的激增,是相对比较均匀的业务消费调用次数

  3. 排查是否是业务执行太慢导致的。在 kafka 堆积问题出现前,业务的消费耗时都是正常的。也不是这个原因导致的堆积。orika消费平均耗时时间orika业务最大耗时

  4. 最后我们分析了实例的堆栈信息。找到对应内存爆满的实例,进入终端,通过 jmap 以及 mat 对实例的堆栈进行分析。orika堆栈信息可以发现 ma.glasnost.orika.generated.Orika_xxx_Mapper 这个的对象占用了 1G 左右的内存,占了整个的 81%。终于,我们找到了问题所在。


问题原因

下面是业务中使用 Orika 的一段代码

1
2
3
4
5
6
7
8
9
10
11
12
private static MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();

// 业务代码中的使用方式
// 每一次调用这个方法,都会去 register 创建一个类似 ma.glasnost.orika.generated.Orika_xxx_Mapper 的对象
// 这样就会导致有很多 ma.glasnost.orika.generated.Orika_xxx_Mapper 这种对象存在在 mapperFactory 中
// 从而造成内存的暴涨
public B test() {
// ...
mapperFactory.classMap(A.class, B.class).field("aField", "bField").byDefault().register();
List<B> bList = mapperFactory.getMapperFacade().mapAsList(aList, B.class);
// ...
}

解决方式

对应 register() 方法只用调用一次就行

1
2
3
4
5
6
7
8
9
10
11
12
private static MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();

// 对应的 mapper 只用在一开始注册就行
static {
mapperFactory.classMap(A.class, B.class).field("aField", "bField").byDefault().register();
}

public B test() {
// ...
List<B> bList = mapperFactory.getMapperFacade().mapAsList(aList, B.class);
// ...
}
作者

zhaommmmomo

发布于

2023-02-03

更新于

2023-06-28

许可协议